package org.squabble.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Version;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.GenericGenerator;

@Entity(name = "comment")
@Table(name = "REMARK")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Comment implements Serializable, Comparable<Comment> {

	private static final long serialVersionUID = 398224499532885864L;

	@Id
	@Column(name="ID")
    @GenericGenerator(name="comment_seq", strategy="increment")
    @GeneratedValue(generator="comment_seq")
	protected Long id;

	@Column(name = "title", length = 255, nullable = true)
	protected String title;

	@Column(name = "TEXT", length = 2000, nullable = false)
	protected String text;

	@Column(name = "SOURCE_DATE")
	protected Date sourceDate;

	@Column(name = "LAST_REPLY")
	protected Date lastReplyDate;

	@Column(name = "DISABLED", nullable = false)
	protected boolean disabled = false;

	@Version
	@Column(name = "VERSION")
	protected Long version;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "ACCOUNT_ID", nullable = false)
	protected Account account;

	@ManyToOne
	@JoinColumn(name = "ARTICLE_ID")
	protected Article article;

	@ManyToOne
	@JoinColumn(name = "PARENT_ID")
	protected Comment parent;

	@OneToMany(fetch = FetchType.LAZY)
	@JoinColumn(name = "PARENT_ID")
	@OrderBy("id")
	protected List<Comment> children = new ArrayList<Comment>();

	@ManyToOne
	@JoinColumn(name = "ROOT_ID")
	protected Comment root;

	@OneToMany(fetch = FetchType.LAZY)
	@JoinColumn(name = "ROOT_ID")
	protected List<Comment> rootChildren = new ArrayList<Comment>();

	@ManyToMany(fetch=FetchType.LAZY)
	@JoinTable(name = "REMARK_FLAG", joinColumns = { @JoinColumn(name = "REMARK_ID") }, inverseJoinColumns = { @JoinColumn(name = "FLAG_ID") })
	@Cascade(CascadeType.SAVE_UPDATE)
	@OrderBy("name")
	private Set<Flag> flags = new LinkedHashSet<Flag>();

	@OneToMany(fetch = FetchType.LAZY)
	@JoinColumn(name = "REMARK_ID")
	@Cascade(CascadeType.SAVE_UPDATE)
	private Set<CommentTag> commentTags = new LinkedHashSet<CommentTag>();

	public Long getId() {
		return id;
	}

	public void setId(Long remarkId) {
		this.id = remarkId;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

	public Date getSourceDate() {
		return sourceDate;
	}

	public void setSourceDate(Date sourceDate) {
		this.sourceDate = sourceDate;
	}

	public Date getLastReplyDate() {
		return lastReplyDate;
	}

	public void setLastReplyDate(Date lastReplyDay) {
		this.lastReplyDate = lastReplyDay;
	}

	public boolean isDisabled() {
		return disabled;
	}

	public void setDisabled(boolean disabled) {
		this.disabled = disabled;
	}

	public Long getVersion() {
		return version;
	}

	public void setVersion(Long version) {
		this.version = version;
	}

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public Article getArticle() {
		return article;
	}

	public void setArticle(Article article) {
		this.article = article;
	}

	public Comment getParent() {
		return parent;
	}

	public void setParent(Comment parent) {
		this.parent = parent;
	}

	public List<Comment> getChildren() {
		return children;
	}

	public void setChildren(List<Comment> children) {
		this.children = children;
	}

	public Comment getRoot() {
		return root;
	}

	public void setRoot(Comment root) {
		this.root = root;
	}

	public List<Comment> getRootChildren() {
		return rootChildren;
	}

	public void setRootChildren(List<Comment> rootChildren) {
		this.rootChildren = rootChildren;
	}

	public Set<Flag> getFlags() {
		return flags;
	}

	public void setFlags(Set<Flag> flags) {
		this.flags = flags;
	}

	public Set<CommentTag> getCommentTags() {
		return commentTags;
	}

	public void setCommentTags(Set<CommentTag> commentTags) {
		this.commentTags = commentTags;
	}

	public class TagSummary implements Serializable, Comparable<TagSummary> {
		private static final long serialVersionUID = -5630442751081827633L;
		private String tag;
		private Integer count = 1;

		public TagSummary(String name) {
			this.tag = name;
		}

		public String getTag() {
			return tag;
		}

		public void setTag(String tag) {
			this.tag = tag;
		}

		public Integer getCount() {
			return count;
		}

		public void setCount(Integer count) {
			this.count = count;
		}

		@Override
		public String toString() {
			return this.tag + "[" + this.count + "]";
		}

		@Override
		public int compareTo(TagSummary o) {
			int cct = o.getCount().compareTo(this.getCount());
			if(cct == 0) {
				return tag.compareToIgnoreCase(o.tag);
			}
			return cct;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((tag == null) ? 0 : tag.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (!(obj instanceof TagSummary))
				return false;
			TagSummary other = (TagSummary) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (tag == null) {
				if (other.tag != null)
					return false;
			} else if (!tag.equals(other.tag))
				return false;
			return true;
		}

		private Comment getOuterType() {
			return Comment.this;
		}

	}

	public List<TagSummary> getTagSummaries() {
		Map<Tag, TagSummary> tags = new HashMap<Tag, TagSummary>();
		for (CommentTag ct : this.commentTags) {
			TagSummary ts = null;
			if (tags.containsKey(ct.getTag())) {
				ts = tags.get(ct.getTag());
				ts.setCount(ts.getCount() + 1);
			} else {
				ts = new TagSummary(ct.getTag().getName());
			}
			tags.put(ct.getTag(), ts);
		}
		List<TagSummary> summaries = new ArrayList<TagSummary>(tags.values());
		Collections.sort(summaries);
		return summaries;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Comment other = (Comment) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override
	public int compareTo(Comment o) {
		// TODO Auto-generated method stub
		return 0;
	}

}
